package middleware

import (
	"fmt"
	"ginblog/utils/errormsg"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	"net/http"
	"strings"
	"time"
)

var JwtKey = []byte("your_access_secret")      // Access Token 密钥
var RefreshKey = []byte("your_refresh_secret") // Refresh Token 密钥

type MyCustomClaims struct {
	Username string `json:"username"`
	jwt.RegisteredClaims
}

// ---------------- Access Token ----------------

// SetAccessToken 生成 Access Token
func SetAccessToken(username string) (string, int) {
	expireTime := jwt.NewNumericDate(time.Now().Add(15 * time.Minute)) // 15分钟有效
	claims := MyCustomClaims{
		Username: username,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: expireTime,
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			Issuer:    "GinBlog",
		},
	}

	tokenObj := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	token, err := tokenObj.SignedString(JwtKey)
	if err != nil {
		return "", errormsg.ERROR
	}
	return token, errormsg.SUCCESS
}

// CheckAccessToken 验证 Access Token
func CheckAccessToken(token string) (*MyCustomClaims, int) {
	tokenObj, err := jwt.ParseWithClaims(token, &MyCustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
		}
		return JwtKey, nil
	})

	if err != nil {
		return nil, errormsg.ERROR
	}

	if claims, ok := tokenObj.Claims.(*MyCustomClaims); ok && tokenObj.Valid {
		return claims, errormsg.SUCCESS
	}
	return nil, errormsg.ERROR
}

// ---------------- Refresh Token ----------------

// SetRefreshToken 生成 Refresh Token
func SetRefreshToken(username string) (string, int) {
	expireTime := jwt.NewNumericDate(time.Now().Add(7 * 24 * time.Hour)) // 7天有效
	claims := MyCustomClaims{
		Username: username,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: expireTime,
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			Issuer:    "GinBlog",
		},
	}

	tokenObj := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	token, err := tokenObj.SignedString(RefreshKey)
	if err != nil {
		return "", errormsg.ERROR
	}
	return token, errormsg.SUCCESS
}

// CheckRefreshToken 验证 Refresh Token
func CheckRefreshToken(token string) (*MyCustomClaims, int) {
	tokenObj, err := jwt.ParseWithClaims(token, &MyCustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
		}
		return RefreshKey, nil
	})

	if err != nil {
		return nil, errormsg.ERROR
	}

	if claims, ok := tokenObj.Claims.(*MyCustomClaims); ok && tokenObj.Valid {
		return claims, errormsg.SUCCESS
	}
	return nil, errormsg.ERROR
}

// JwtTwoToken 中间件函数
func JwtTwoToken() gin.HandlerFunc {
	return func(c *gin.Context) {
		tokenHeader := c.Request.Header.Get("Authorization")
		code := errormsg.SUCCESS

		if tokenHeader == "" {
			code = errormsg.ERROR_TOKEN_NOT_EXIST
			c.JSON(http.StatusOK, gin.H{"code": code, "message": errormsg.GetErrorMsg(code)})
			c.Abort()
			return
		}

		checkToken := strings.SplitN(tokenHeader, " ", 2)
		if len(checkToken) != 2 || checkToken[0] != "Bearer" {
			code = errormsg.ERROR_TOKEN_TYPE_WRONG
			c.JSON(http.StatusOK, gin.H{"code": code, "message": errormsg.GetErrorMsg(code)})
			c.Abort()
			return
		}

		key, tCode := CheckAccessToken(checkToken[1])
		if tCode != errormsg.SUCCESS {
			code = errormsg.ERROR_TOKEN_WRONG
			c.JSON(http.StatusOK, gin.H{"code": code, "message": errormsg.GetErrorMsg(code)})
			c.Abort()
			return
		}

		if key.ExpiresAt != nil && time.Now().After(key.ExpiresAt.Time) {
			code = errormsg.ERROR_TOKEN_RUNTIME
			c.JSON(http.StatusOK, gin.H{"code": code, "message": errormsg.GetErrorMsg(code)})
			c.Abort()
			return
		}

		c.Set("username", key.Username)
		c.Next()
	}
}
